package org.NooLab.glue.filter;

import java.util.Vector;

import org.NooLab.compare.utilities.math.DistanceControlProperties;
import org.NooLab.glue.MessageBoard;
import org.NooLab.glue.components.MessageBoardFactory;
import org.NooLab.glue.msgframe.MetaRegistration;
import org.NooLab.glue.msgframe.XMessage;
import org.NooLab.glue.msgframe.XRegistration;
import org.NooLab.glue.subscription.FilterPackages;
import org.NooLab.glue.subscription.FutureIntf;
import org.NooLab.glue.subscription.FutureProperties;
import org.NooLab.glue.subscription.FuturesIntf;
import org.NooLab.glue.subscription.context.Context;
import org.NooLab.glue.subscription.context.ContextIntf;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.objects.StringedObjects;



public class SubscriptionHandlerInterpreter extends MessageInterpreter{

	 
	
	String rawXmlMsg;
	
	 
	
	public SubscriptionHandlerInterpreter( 	MessageBoard messageboard, 
			   								FilterPackages filters, 
			   								PrintLog outprn){
		super( messageboard, filters, outprn );

		
	}
	
	public SubscriptionHandlerInterpreter( MessageBoardFactory mbf, 
			 							   FilterPackages filters, 
			 							   PrintLog outprn){
		super( mbf, filters, outprn );
		 
		 
	}
	
	
	public int go( String rawxmlmsg ){
		
		int result= -1, p1;
		
		String[] rawIndicators ;
		XMessage xmsg ;
		
		xmsg = new XMessage(out);
		xmsg.setContentRoot("subscription");
		
		
		rawXmlMsg = rawxmlmsg;
		
		p1 = rawXmlMsg.indexOf("<subscription");
		
		if (p1>0){
			// we are dealing with an ongoing transaction
			result = 0;

			rawIndicators = new String[] { "<instance", "action=\"def", "<instance", "<properties" };
			if (sourceVariant_ReceivingSubscription(xmsg, rawXmlMsg, rawIndicators)) { return result; }

			rawIndicators = new String[] { "<instance", "action=\"get" };
			if (sourceVariant_RequestForDeliveryOfSubscriptionList(xmsg, rawXmlMsg, rawIndicators)) { return result; }
			
			rawIndicators = new String[] { "<instance", "action=\"unpub", "<instance" };
			if (sourceVariant_UnpublishSubscription(xmsg, rawXmlMsg, rawIndicators)) { return result; }
			
			
		}
		return result ;
	}

	
	@SuppressWarnings("unused")
	private boolean sourceVariant_ReceivingSubscription( XMessage xmsg, String rawXmlMsg, String[] rawIndicators ){
		
		/*
			<messageboard name="spela" role="SOURCE">
				<subscription action="define">
  				<instance id="2528" name="" />
  				<properties>
     			
      				<property id="0" type="content" label="keywords" values="a;b;c"/>
      				<property id="1" type="control" label="weights" values="1;0.4;0"/>
      				<property id="2" type="control" label="thinning" values="0.4;0.2"/>
      				<property id="3" type="control" label="threshold" values="0.63"/>
   
  				</properties>
				</subscription>
			</messageboard>
		*/
		// get parameters, (re)create an Object and then delegate it to messageBoard's subscription admin
		
		boolean result;
		long iid;
		int pid,ix, typeValue;
		String clientName="",actionStr="" ,typestr, str,dstr, vstr;
		
		XRegistration xreg ;
		
		ContextIntf context=null;
		
		FuturesIntf futures=null;
		FutureIntf subscription=null;
		
		
		result = xmsg.rawIndicatorsCheck( rawXmlMsg, rawIndicators);
		
		if ( result ){
			
			iid = xmsg.getSpecifiedValue(rawXmlMsg, "/instance", "id");
			
			ix = metaRegistration.indexOf(iid) ;
			
			if (ix>=0){
				
				xreg = metaRegistration.getItem(ix) ;
				futures = messageBoard.getFutures() ;  
			}else{
				
				return true;
			}
			
			clientName = xmsg.getSpecifiedInfo(rawXmlMsg, "/instance", "name");

			actionStr = xmsg.getSpecifiedInfo(rawXmlMsg, "/subscription", "action");
			
			if ((iid>0) && (clientName.length()>0) && (actionStr.toLowerCase().startsWith("def"))){
				
				typeValue = (int) xmsg.getSpecifiedValue(rawXmlMsg, "//properties", "type");
			 	
				if (typeValue == FutureProperties._SIMPLESET){ 
					subscription = establishSimpleSubscriptionFilter( xmsg, futures, rawXmlMsg ) ;
				}
				
				if (typeValue == FutureProperties._VECTOR){ 
					subscription = establishFeatureVectorSubscriptionFilter( xmsg, futures, rawXmlMsg ) ;
				}
				if (typeValue==FutureProperties._CONTEXT){
					subscription = establishContextbasedSubscriptionFilter( xmsg, futures, rawXmlMsg ) ;
				}

				if (typeValue == FutureProperties._FREETEXT){ 
					subscription = establishFullTextSubscriptionFilter( xmsg, futures, rawXmlMsg ) ;
				}
				if (typeValue == FutureProperties._IMAGE){ 
					subscription = establishImageSubscriptionFilter( xmsg, futures, rawXmlMsg ) ;
				}

				
				if ((subscription != null) && (futures!=null)){
					// adding the reference to the registration -> in unregistering, remove also the subscription !!
					subscription.setInstanceID(iid) ;
					
					String regGuid = subscription.getGuid() ;
												out.print(2, "registered subscription : "+regGuid) ;
					futures.add( subscription );
					
					// futures are part of the MessageBoard (but created by the MessageBoardFactory)
					String ps = "s";
					String vs = "are" ;
					int n = futures.size(iid);
					if (n==1){
						ps=""; vs="is" ;
					}
												out.print(2, "subscription added for instance id="+iid+", now there "+vs+" "+
															 n+" (si:"+futures.size( FutureProperties._SIMPLESET,iid)+
															 ",co:"+futures.size(FutureProperties._CONTEXT,iid)+") subscription"+ps+" for this instance.") ;
				} 
				
				// return a confirmation ?
			} // iid ok?
		} // raw content check ?

		return result ;
	}

	
	private boolean sourceVariant_RequestForDeliveryOfSubscriptionList( XMessage xmsg, String rawXmlMsg, String[] rawIndicators ){
	
		boolean result;
		
		result = xmsg.rawIndicatorsCheck( rawXmlMsg, rawIndicators);
		
		if ( result ){
			
			
		}
	
		return result ;
	}


	private boolean sourceVariant_UnpublishSubscription( XMessage xmsg, String rawXmlMsg, String[] rawIndicators ){
	
		boolean result;
		
		String actionStr,subguid="" ;
		long iid;
		int ix,n0;

		FuturesIntf futures=null;
	 
		

		result = xmsg.rawIndicatorsCheck( rawXmlMsg, rawIndicators);
		
		if ( result ){
				
				iid = xmsg.getSpecifiedValue(rawXmlMsg, "/instance", "id");
				
				ix = metaRegistration.indexOf(iid) ;
				
				if (ix>=0){
					 
					futures = messageBoard.getFutures() ;  
				}

				if (futures==null){
					return result;
				}
				n0 = futures.size(iid);
				subguid = xmsg.getSpecifiedInfo(rawXmlMsg, "/subscription", "name");
				
				actionStr = xmsg.getSpecifiedInfo(rawXmlMsg, "/subscription", "action");
				
				if (actionStr.toLowerCase().startsWith("unpub")){
					
					
					
					if (futures!=null){
						futures.remove(subguid);
					}
				}
				
				String ps = "s";
				String vs = "are" ;
				int n = futures.size(iid);
				if (n==1){
					ps=""; vs="is" ;
				}
				if (n0==n){
					out.print(2, "Unpublishing subscription for instance id="+iid+" failed, now there "+vs+" "+
							 n+" (si:"+futures.size( FutureProperties._SIMPLESET,iid)+
							 ",co:"+futures.size(FutureProperties._CONTEXT,iid)+") subscription"+ps+" for this instance.") ;
					
				}else{
					out.print(2, "subscription (id:"+subguid+") unpublished for instance id="+iid+", \n"+
							     "             now there "+vs+" "+
								 n+" (si:"+futures.size( FutureProperties._SIMPLESET,iid)+
								 ",co:"+futures.size(FutureProperties._CONTEXT,iid)+") subscription"+ps+" for this instance.") ;
				}
		}
	
		return result ;
	}
		
	 


	private DistanceControlProperties getParamsForDistanceCtrl( XMessage xmsg, String rawXmlMsg ){
		
		/*
		 	<distanceControl>
	        	<missingvalue handling="2" method="2"/>
	        	<surrogatdistance value="0.15"/>
	        	<itemsetcontrast alpha="0.25" beta="0.25" sigma="0.25"/>
	        	<alignment  bypassmode="1" />
	     	</distanceControl>
		 */
		 
		String str;
		int value ;
		boolean hb;
		double v;
		
		DistanceControlProperties _distanceCtrl ;
		
		try{
		
			_distanceCtrl = new DistanceControlProperties() ;
			
			value = (int) xmsg.getSpecifiedValue(rawXmlMsg, "//distanceControl/missingvalue", "handling");
				    _distanceCtrl.setDistanceMethod(value) ;
	
			value = (int) xmsg.getSpecifiedValue(rawXmlMsg, "//distanceControl/missingvalue", "method");
		    		_distanceCtrl.setMissingValueHandling(value);
			
			str = xmsg.getSpecifiedInfo(rawXmlMsg, "//distanceControl/surrogatdistance", "value");
			  	  if ((str.length()>0) && (strgutil.isNumericX(str))){
			  		  v = Double.parseDouble(str) ;
			  		  _distanceCtrl.setSurrogateDistance(v) ;
			  	  }
			
			  	  
			str = xmsg.getSpecifiedInfo(rawXmlMsg, "//distanceControl/alignment", "bypassmode");
				  if ((str.length()>0) && (strgutil.isNumericX(str))){
					  hb = (str.contentEquals("0")==false) ;
					  _distanceCtrl.setBypassVectorAlignCheck(hb) ;
				  }
			  	 
			str = xmsg.getSpecifiedInfo(rawXmlMsg, "//distanceControl/itemsetcontrast", "alpha");
				  if ((str.length()>0) && (strgutil.isNumericX(str))){
					  v = Double.parseDouble(str) ;
					  _distanceCtrl.setItemSetContrastalpha( v);
				  }
	
			str = xmsg.getSpecifiedInfo(rawXmlMsg, "//distanceControl/itemsetcontrast", "beta");
			      if ((str.length()>0) && (strgutil.isNumericX(str))){
			    	  v = Double.parseDouble(str) ;
			    	  _distanceCtrl.setItemSetContrastbeta( v);
			      }
			
			str = xmsg.getSpecifiedInfo(rawXmlMsg, "//distanceControl/itemsetcontrast", "sigma");
				  if ((str.length()>0) && (strgutil.isNumericX(str))){
					  v = Double.parseDouble(str) ;
					  _distanceCtrl.setItemSetContrastsigma( v);
				  }
			str = xmsg.getSpecifiedInfo(rawXmlMsg, "//distanceControl/itemsetcontrast", "method");
				  if ((str.length()>0) && (str.startsWith("-")==false) && (strgutil.isNumericX(str))){
					  value = Integer.parseInt(str) ;
					  _distanceCtrl.setSetSimMethod(value) ;
				  }
			
		}catch(Exception e){
			_distanceCtrl = new DistanceControlProperties();
		}
		return _distanceCtrl;
	}

	private FutureIntf establishSimpleSubscriptionFilter( XMessage xmsg, FuturesIntf futures, String rawXmlMsg ){
		
		FutureIntf subscription=null;
		DistanceControlProperties _distanceCtrl =null; 
		
		int pid;
		String str,dstr="",vstr="",guidStr="", xvstr="",namestr="", labelStr="";
		double mmv=1.0;
		long iid;
		
		Vector<Object> listItemsObj, itemsObj ;
		
		String[] values = new String[0];
		String[] xvalues = new String[0];
		Object obj;
		
		try{

			namestr = xmsg.getSpecifiedInfo(rawXmlMsg, "//properties", "id");
			guidStr = namestr; 
			
			iid = xmsg.getSpecifiedValue(rawXmlMsg, "//instance","id");
			listItemsObj = xmsg.getItemsList( rawXmlMsg, "//properties", "/property" ,"id" );
		  /*
				<properties id="f87411e3-a606-4934-b395-3408dead0fb5" type="1">
         			<distanceControl>
         			   	<missingvalue handling="2" method="2"/>
           			 	<surrogatdistance value="0.15"/>
           			 	<itemsetcontrast alpha="0.25" beta="0.25" sigma="0.25"/>
           			 	.e("alignment").a("bypassmode", ""+alignmentBypassValue).up()
         			</distanceControl>
         
         			<property id="1" label="keywords">
           			 	<items type="incl" values="x;s;a"/>
            			<items type="excl" values="m"/>      
         			</property>

         			<property id="2" label="match">
            			<items type="threshold" value="0.5"/>
         			</property>
      			</properties>
		  */
			
			_distanceCtrl = getParamsForDistanceCtrl( xmsg, rawXmlMsg );
			 
			// now retrieving properties one after another
			for (int i=0;i<listItemsObj.size();i++){
			
				str = (String)listItemsObj.get(i);
				
				if (str.length() == 0) {
					continue;
				}

					pid = Integer.parseInt(str);

				
					labelStr = xmsg.getSpecifiedInfo( rawXmlMsg, "//properties/property", "id",""+pid, "label");

					if (labelStr.startsWith("keyword")) {
						// now we need the list of all tags beyond <property id="i">, we get it as a list of node objects

						itemsObj = xmsg.selectListFromSpecifiedItem( rawXmlMsg, "//properties/property" ,"id", "1", "items" );

						if (itemsObj==null){
							continue;
						}
						
						for (int n=0;n<itemsObj.size();n++){
							obj = itemsObj.get(n) ;
							
							dstr = xmsg.getSpecifiedItemInfo(obj, "type");
							
							if (dstr.startsWith("incl")){
								vstr = xmsg.getSpecifiedItemInfo(obj, "values");
							}
							if (dstr.startsWith("excl")){
								xvstr = xmsg.getSpecifiedItemInfo(obj, "values");
							}
							
						} // n->

					} // "keyword" ?
					
					if (labelStr.startsWith("match")) {

						itemsObj = xmsg.selectListFromSpecifiedItem( rawXmlMsg, "//properties/property" ,"id", "2", "items" );

						for (int n=0;n<itemsObj.size();n++){
							obj = itemsObj.get(n) ;
							
							dstr = xmsg.getSpecifiedItemInfo(obj, "type");
							
							if (dstr.startsWith("threshold")){
								str = xmsg.getSpecifiedItemInfo(obj, "value");
								if (str.length() > 0) {
									mmv = Double.parseDouble(str);
								}
							} // "threshold" ?
						} // n->
						
					} // "match" ?

					
				 
			} // i->
			
			if (vstr.length()>0){
				values = vstr.split(";");
			}

			if (xvstr.length()>0){
				xvalues = xvstr.split(";");
			}
			
			if ( (labelStr.length()>0) && (vstr.length()>0)){
				if (futures!=null){
					subscription = futures.create( mbf.getMsgBoardShare().getCompareKernel().getDistanceCtrl() );
					
					subscription = futures.create( _distanceCtrl );
					
					subscription.setGuid(guidStr) ;
					subscription.setInstanceID(iid) ;
					 
					subscription.addProperty( namestr, values, xvalues, mmv) ;
				}
			}
			
			subscription.setDistanceControlProperties( new DistanceControlProperties(_distanceCtrl) );

			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return subscription;
	}

	
	
	private FutureIntf establishFeatureVectorSubscriptionFilter( XMessage xmsg, FuturesIntf futures, String rawXmlMsg) {
		
		FutureIntf subscription=null;
		DistanceControlProperties _distanceCtrl =null; 
		
		int pid;
		String str,dstr="",vfstr="",vnstr="",guidStr="", xvstr="",namestr="", labelStr="";
		String subscriptName="";
		double mmv=0.1, msv=0.1;
		long iid;
		
		Vector<Object> listItemsObj, itemsObj ;
		
		String[] values = new String[0];
		String[] ifvalues = new String[0];
		String[] xvalues = new String[0];
		double[] numdatavalues = new double[0];
		
		Object obj;
		
		try{

			namestr = xmsg.getSpecifiedInfo(rawXmlMsg, "//properties", "id");
			
			guidStr = namestr;
			
			iid = xmsg.getSpecifiedValue(rawXmlMsg, "//instance","id");
			
				
			listItemsObj = xmsg.getItemsList( rawXmlMsg, "//properties", "/property" ,"id" );
		  /*
				<properties id="45ffb8a2-e68a-49cb-8b95-25f04f253d3d" type="2">
				
         			<distanceControl>
            			<missingvalue handling="2" method="2"/>
            			<surrogatdistance value="0.15"/>
            			<itemsetcontrast alpha="0.25" beta="0.25 sigma="0.5" method="2" />
            			<alignment  bypassmode="1" />
         			</distanceControl>
         			
         			<property id="1" label="profile X">
            			<items type="features" values="x;s;a"/>
            			<items type="numdata" values="0.2;0.8;0.4"/>
            			<items type="excl" values="m*"/>
         			</property>

         			<property id="2" label="threshold">
            			<items type="match" value="0.7"/>
            			<items type="similarity" value="0.7"/>
         			</property>
         			
      			</properties> 
		  */
			 
			

			_distanceCtrl = getParamsForDistanceCtrl( xmsg, rawXmlMsg );
			
			_distanceCtrl.setNormalizedDistances(true);
			_distanceCtrl.setRelativeDistances(true) ;
			   
			// now retrieving properties one after another
			for (int i=0;i<listItemsObj.size();i++){
			
				str = (String)listItemsObj.get(i);
				
				if (str.length() == 0) {
					continue;
				}

					pid = Integer.parseInt(str);

				
					labelStr = xmsg.getSpecifiedInfo( rawXmlMsg, "//properties/property", "id",""+pid, "label");

					subscriptName = xmsg.getSpecifiedInfo( rawXmlMsg, "//properties/property", "id",""+pid, "name");
					
					if (labelStr.startsWith("feature")) {
						// now we need the list of all tags beyond <property id="i">, we get it as a list of node objects

						itemsObj = xmsg.selectListFromSpecifiedItem( rawXmlMsg, "//properties/property" ,"id", "1", "items" );

						if (itemsObj==null){
							continue;
						}
						
						for (int n=0;n<itemsObj.size();n++){
							obj = itemsObj.get(n) ;
							
							dstr = xmsg.getSpecifiedItemInfo(obj, "type");
							
							if (dstr.startsWith("features")){
								vfstr = xmsg.getSpecifiedItemInfo(obj, "values");
							}
							if (dstr.startsWith("numdata")){
								vnstr = xmsg.getSpecifiedItemInfo(obj, "values");
							}
							if (dstr.startsWith("excl")){ 
								xvstr = xmsg.getSpecifiedItemInfo(obj, "values");
							}
						} // n->

					} // "keyword" ?
		 		
					if (labelStr.startsWith("threshold")) {

						itemsObj = xmsg.selectListFromSpecifiedItem( rawXmlMsg, "//properties/property" ,"id", "2", "items" );

						for (int n=0;n<itemsObj.size();n++){
							obj = itemsObj.get(n) ;
							
							dstr = xmsg.getSpecifiedItemInfo(obj, "type");
							
							if (dstr.startsWith("match")){
								str = xmsg.getSpecifiedItemInfo(obj, "value");
								if (str.length() > 0) {
									mmv = Double.parseDouble(str);
								}
							} // "match" ?
							if (dstr.startsWith("similarity")){
								str = xmsg.getSpecifiedItemInfo(obj, "value");
								if (str.length() > 0) {
									msv = Double.parseDouble(str);
								}
							} // "similarity" ?
							
						} // n->
						
					} // "match" ?

				 
			} // i->
			
			if (vfstr.length()>0){
				ifvalues = vfstr.split(";");
				 
			}

			if (vnstr.length()>0){
				values = vnstr.split(";");
				numdatavalues = strgutil.changeArrayType(values, -1.0, true) ;
			}
			
			if (xvstr.length()>0){
				xvalues = xvstr.split(";");
			}
			
			if ( (labelStr.length()>0) && (values.length >0) && (numdatavalues.length >0)){
				if (futures!=null){
					subscription = futures.create( _distanceCtrl );
					
					subscription.setGuid(guidStr) ;
					subscription.setInstanceID(iid) ;
					
					subscription.addProperty( subscriptName,  // label 
											  ifvalues,    	  // feature items (fields) of the vector
											  numdatavalues,  // the numerical values
											  xvalues, 		  // optional: excluding features, suffix-wildcards are allowed here!
											  mmv, msv ) ;    // minimal match value, relative to max distance  
				}
			}
			 
			
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return subscription;
	}

	private FutureIntf establishContextbasedSubscriptionFilter( XMessage xmsg,FuturesIntf futures, String rawXmlMsg ){
		
		String encodedObjStr, namestr;
		long iid;
		ContextIntf context=null;
		StringedObjects strgObj ;
		FutureIntf subscription=null;
	
		
		strgObj = new StringedObjects() ;
		
		namestr = xmsg.getSpecifiedInfo(rawXmlMsg, "//properties", "id");
		
		iid = xmsg.getSpecifiedValue(rawXmlMsg, "//instance","id");
		
		encodedObjStr = xmsg.getIncludedXText( rawXmlMsg, "//properties/context");
		
		if (encodedObjStr.length()>0){
			context = (ContextIntf) strgObj.decode(encodedObjStr) ;
			
		} // encoded object found ?
		
		if (futures!=null){
			subscription = futures.create( mbf.getMsgBoardShare().getCompareKernel().getDistanceCtrl());
			subscription.setGuid(namestr) ;
			
			subscription.setInstanceID(iid) ;
			subscription.addProperty( "", context );
		}
		return subscription;
	}

	
	private FutureIntf establishFullTextSubscriptionFilter(XMessage xmsg, FuturesIntf futures, String rawXmlMsg) {
		 
		return null;
	}

	private FutureIntf establishImageSubscriptionFilter(XMessage xmsg, FuturesIntf futures, String rawXmlMsg) {
		 
		return null;
	}

	
	
	public Context rebuildContextFromXml(String rawXmlMsg2) {
		 
		return null;
	}
	
}
